package com.wyt.cloud.utils.designpatterns.dutychain;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.List;

public abstract class AbstractChainPipeline<T> implements ApplicationContextAware, InitializingBean {

    private static final IHandler DEFAULT_HANDLER = t -> {};

    private ApplicationContext applicationContext;

    @Autowired
    private List<IHandler<T>> handlers;

    protected HandlerContext<T> head;
    private HandlerContext<T> tail;

    //    public abstract void start(T t);
    public void start(T t) {
        head.next(t);
    }

    /**
     * 构建一个头节点A和一个尾节点B
     * A的后继节点指向B
     * B的前驱节点指向A
     * 将所有节点按照@Order的顺序load到链表中来
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() {
        head = newHandlerContext(DEFAULT_HANDLER);
        tail = newHandlerContext(DEFAULT_HANDLER);
        head.nextContext = tail;
        tail.preContext = head;
        for (IHandler<T> handler : handlers) {
            buildHandlerChain(handler);
        }
    }

    /**
     * 调用链构建---创建一个节点handler,
     * tail的前驱节点A的后继指针指向handler
     * handler的前驱指针指向A
     * handler的后继指向tail
     *
     * @param handler
     */
    public void buildHandlerChain(IHandler<T> handler) {
        HandlerContext<T> handlerContext = newHandlerContext(handler);
        tail.preContext.nextContext = handlerContext;
        handlerContext.preContext = tail.preContext;
        handlerContext.nextContext = tail;
        tail.preContext = handlerContext;
    }

    private HandlerContext<T> newHandlerContext(IHandler<T> handler) {
        HandlerContext<T> context = applicationContext.getBean(HandlerContext.class);
        context.setHandler(handler);
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
